package com.gitee.wsl.io

import com.gitee.wsl.ext.base.ifTrue
import com.gitee.wsl.ext.base.isFalse
import kotlinx.io.RawSink
import kotlinx.io.RawSource
import kotlinx.io.Sink
import kotlinx.io.Source
import kotlinx.io.buffered
import kotlinx.io.files.FileSystem
import kotlinx.io.files.Path
import kotlinx.io.files.SystemFileSystem


inline fun Path.source(fileSystem: FileSystem=SystemFileSystem,block: RawSource.()->Unit) = fileSystem.source(this).use(block)

fun Path.sink(fileSystem: FileSystem=SystemFileSystem,append: Boolean = false) = fileSystem.sink(this,append)

inline fun Path.sink(fileSystem: FileSystem=SystemFileSystem,append: Boolean = false,block: RawSink.()->Unit) = fileSystem.sink(this,append).use(block)

inline fun Path.sourceBuffered(fileSystem: FileSystem=SystemFileSystem,block: Source.()->Unit) = sourceBuffered(fileSystem).use(block)

inline fun Path.sinkBuffered(fileSystem: FileSystem=SystemFileSystem,append: Boolean = false,block: Sink.()->Unit) = sinkBuffered(fileSystem,append).use(block)

 fun Path.sourceBuffered(fileSystem: FileSystem=SystemFileSystem,) = fileSystem.source(this).buffered()

 fun Path.sinkBuffered(fileSystem: FileSystem=SystemFileSystem,append: Boolean = false) = fileSystem.sink(this, append).buffered()

 fun Path.exists(fileSystem: FileSystem=SystemFileSystem) = fileSystem.exists(this)

 fun Path.delete(fileSystem: FileSystem=SystemFileSystem) = runCatching { fileSystem.delete(this, mustExist = false) }.isSuccess

 fun Path.deleteIfExists(fileSystem: FileSystem=SystemFileSystem) = exists().ifTrue { fileSystem.delete(this,true)  }

fun Path.deleteRecursively(fileSystem: FileSystem=SystemFileSystem): Boolean {
    if (fileSystem.metadataOrNull(this)?.isDirectory == true) {
        fileSystem.list(this).forEach { it.deleteRecursively() }
    }
    return delete()
}

fun Path.isDirectory(fileSystem: FileSystem=SystemFileSystem,) = fileSystem.metadataOrNull(this)?.isDirectory == true

fun Path.isRegularFile(fileSystem: FileSystem=SystemFileSystem,) = fileSystem.metadataOrNull(this)?.isRegularFile == true

fun Path.createDirectoriesIfNoExists(fileSystem: FileSystem=SystemFileSystem)
     = exists().isFalse { fileSystem.createDirectories(this,false)  }

fun Path.requireFile(fileSystem: FileSystem = SystemFileSystem) {
    if (fileSystem.metadataOrNull(this)?.isRegularFile != true) {
        throw IllegalArgumentException("File does not exist or is not a regular file: $this")
    }
}

fun String.toPath() = Path(this)

val Path.isPhysicalFile:Boolean get() = toString().first() == '/'

private val HEX_DIGITS =
    charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')

private const val PATH_SEGMENT_ALWAYS_ENCODE_SET = " \"<>^`{}|\\?#"
/*

fun String.canonicalizeForPath(alreadyEncoded: Boolean): String {
    var codePoint: Int
    var i = 0
    val limit = length
    while (i < limit) {
        codePoint = codePointAt(i)
        if (codePoint < 0x20 ||
            codePoint >= 0x7f ||
            codePoint.toChar() in PATH_SEGMENT_ALWAYS_ENCODE_SET ||
            !alreadyEncoded && (codePoint == '/'.code || codePoint == '%'.code)
        ) {
            // Slow path: the character at i requires encoding!
            val out = Buffer()
            out.writeString(this,Charsets.UTF_8,0,i)
            //out.writeUtf8(this, 0, i)
            out.canonicalizeForPath(this, i, limit, alreadyEncoded)
            //return out.readUtf8()
            return out.readString(Charsets.UTF_8)
        }
        i += Character.charCount(codePoint)
    }

    // Fast path: no characters required encoding.
    return this
}

fun Buffer.canonicalizeForPath(
    input: String,
    pos: Int,
    limit: Int,
    alreadyEncoded: Boolean
) {
    var utf8Buffer: Buffer? = null // Lazily allocated.
    var codePoint: Int
    var i = pos
    while (i < limit) {
        codePoint = input.codePointAt(i)
        if (alreadyEncoded && (codePoint == '\t'.code || codePoint == '\n'.code || codePoint == '\u000c'.code || codePoint == '\r'.code)) {
            // Skip this character.
        } else if (codePoint < 0x20 ||
            codePoint >= 0x7f ||
            codePoint.toChar() in PATH_SEGMENT_ALWAYS_ENCODE_SET ||
            !alreadyEncoded && (codePoint == '/'.code || codePoint == '%'.code)
        ) {
            // Percent encode this character.
            if (utf8Buffer == null) {
                utf8Buffer = Buffer()
            }

            utf8Buffer.writeUtf8CodePoint(codePoint)

            while (!utf8Buffer.exhausted()) {
                val b: Int = utf8Buffer.readByte().toInt() and 0xff
                writeByte('%'.code)
                writeByte(HEX_DIGITS[b shr 4 and 0xf].code)
                writeByte(HEX_DIGITS[b and 0xf].code)
            }
        } else {
            // This character doesn't need encoding. Just copy it over.
            writeUtf8CodePoint(codePoint)
        }
        i += Character.charCount(codePoint)
    }
}*/

operator fun Path.div(name:String) = Path(this,name)

operator fun Dir.div(name:String) = Path(this.path,name)

fun Path.size(fileSystem: FileSystem = SystemFileSystem) = fileSystem.metadataOrNull(this)?.size ?: 0L

fun Path.ensureDirectory(fileSystem: FileSystem=SystemFileSystem) {
    if (fileSystem.metadataOrNull(this)?.isDirectory != true) {
        fileSystem.delete(this, mustExist = false)
        fileSystem.createDirectories(this)
    }
}

